;;; init-lang.el --- Programming language settings -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

;; emacs treesit 支持
(use-package treesit
  :when (and (fboundp 'treesit-available-p) (treesit-available-p))
  :ensure nil
  :config
  (setq treesit-font-lock-level 4) ; 高亮级别

  ;; M-x `treesit-install-language-grammar` to install language grammar.
  (setq treesit-language-source-alist
        '((c          . ("https://github.com/tree-sitter/tree-sitter-c"))
          (cmake      . ("https://github.com/uyha/tree-sitter-cmake"))
          (cpp        . ("https://github.com/tree-sitter/tree-sitter-cpp"))
          (elisp      . ("https://github.com/Wilfred/tree-sitter-elisp"))
          (fish       . ("https://github.com/ram02z/tree-sitter-fish"))
          (go         . ("https://github.com/tree-sitter/tree-sitter-go"))
          (haskell    . ("https://github.com/tree-sitter/tree-sitter-haskell"))
          (java       . ("https://github.com/tree-sitter/tree-sitter-java.git"))
          (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript"))
          (json       . ("https://github.com/tree-sitter/tree-sitter-json"))
          (make       . ("https://github.com/alemuller/tree-sitter-make"))
          (markdown   . ("https://github.com/MDeiml/tree-sitter-markdown" nil "tree-sitter-markdown/src"))
          (org        . ("https://github.com/milisims/tree-sitter-org"))
          (python     . ("https://github.com/tree-sitter/tree-sitter-python"))
          (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "typescript/src"))
          (rust       . ("https://github.com/tree-sitter/tree-sitter-rust"))
          (sql        . ("https://github.com/m-novikov/tree-sitter-sql"))
          (vue        . ("https://github.com/merico-dev/tree-sitter-vue"))
          (yaml       . ("https://github.com/ikatyang/tree-sitter-yaml"))
          (zig        . ("https://github.com/GrayJack/tree-sitter-zig"))))

  ;; replace xx-mode to xx-ts-mode
  (setq major-mode-remap-alist
        '((c-mode          . c-ts-mode)
          (c++-mode        . c++-ts-mode)
          (cmake-mode      . cmake-ts-mode)
          (java-mode       . java-ts-mode)
          (js-mode         . js-ts-mode)
          (json-mode       . json-ts-mode)
          (python-mode     . python-ts-mode)
          (rust-mode       . rust-ts-mode)
          (typescript-mode . typescript-ts-mode)
          (yaml-mode       . yaml-ts-mode)))

  ;; others
  (add-hook 'emacs-lisp-mode-hook (lambda () (treesit-parser-create 'elisp)))
  (add-hook 'fish-mode-hook (lambda () (treesit-parser-create 'fish)))
  (add-hook 'haskell-mode-hook (lambda () (treesit-parser-create 'haskell)))
  (add-hook 'markdown-mode-hook (lambda () (treesit-parser-create 'markdown)))
  (add-hook 'org-mode-hook (lambda () (treesit-parser-create 'org)))
  (add-hook 'sql-mode-hook (lambda () (treesit-parser-create 'sql)))
  (add-hook 'web-mode-hook (lambda ()
                             (let ((file-name (buffer-file-name)))
                               (when file-name
                                 (treesit-parser-create
                                  (pcase (file-name-extension file-name)
                                    ("vue" 'vue)
                                    ))))))
  (add-hook 'zig-mode-hook (lambda () (treesit-parser-create 'zig))))

(defun +compile/c-compile-setup ()
  "Set up compile-command for C/C++ files, unless Makefile exists."
  (when (and (buffer-file-name) ; 确保是有文件关联的 buffer
             (not (or (file-exists-p "makefile")
                      (file-exists-p "Makefile"))))
    (let* ((is-c++ (derived-mode-p 'c++-mode 'c++-ts-mode))
           (src-file (buffer-file-name))
           (out-file (file-name-sans-extension src-file))
           (compiler (if is-c++ "g++" "gcc")))
      (setq-local compile-command
                  (format "%s -g -Wall -Wextra %s -o %s"
                          compiler
                          (shell-quote-argument src-file)
                          (shell-quote-argument out-file))))))

(use-package cc-mode
  :ensure nil
  :mode (("\\.\\(c\\|h\\)\\'" . c-mode)
         ("\\.\\(cpp\\|hpp\\)\\'" . c++-mode))
  :hook ((c-mode c++-mode c-ts-mode c++-ts-mode) .
         (lambda()
           (+compile/c-compile-setup)
           (setq-local comment-start "//"
                       comment-end "")
           (local-set-key (kbd "C-<f8>") 'gdb))))

(use-package fish-mode
  :ensure t
  :mode ("\\.fish\\'" . fish-mode))

(use-package f90
  :ensure nil
  :mode ("\\.f90\\'" . f90-mode))

;; 数学绘图支持
(use-package gnuplot
  :ensure t
  :mode ("\\.gp$" . gnuplot-mode))

(use-package haskell-mode
  :ensure t
  :mode ("\\.\\(hs\\|lhs\\|hsc\\|cpphs\\|c2hs\\|chs\\)\\'" . haskell-mode)
  :hook ((haskell-mode . haskell-indentation-mode)
         (haskell-mode . haskell-doc-mode)))

(use-package javascript-mode 
  :ensure nil
  :mode ("\\.js\\'" . javascript-mode))

(use-package json-mode
  :ensure t
  :mode ("\\.json\\'" . json-mode))

(use-package makefile-mode
  :ensure nil
  :hook ((makefile-mode . indent-tabs-mode)
         (makefile-mode . whitespace-mode)))

(use-package markdown-mode
  :ensure t
  :mode (("\\.md\\'" . markdown-mode)
         ("README\\.md\\'" . gfm-mode))
  :bind ((:map markdown-mode-map
               ("C-c C-z" . #'markdown-view-mode))
         (:map gfm-view-mode-map
               ("q" . #'quit-window))
         (:map markdown-view-mode-map
               ("C-c C-z" . #'markdown-mode)
               ("q" . #'quit-window))))

(use-package mermaid-mode
  :ensure t
  :mode "\\.mmd\\'")

;; plantuml 支持
(use-package plantuml-mode
  :ensure t
  :mode ("\\.plantuml\\'" . plantuml-mode))

(use-package nginx-mode
  :ensure t
  :hook (nginx-mode . whitespace-mode)
  :init
  (setq nginx-indent-tabs-mode nil)
  (add-to-list 'auto-mode-alist
               '("/nginx/sites-\\(?:available\\|enabled\\)/" . nginx-mode)))

(use-package properties-mode
  :ensure (:host github :repo "iquiw/properties-mode")
  :mode ("\\.properties\\'" . properties-mode))

(use-package python-mode
  :ensure nil
  :interpreter "python"                 ; 根据文件的 shebang 行启动模式
  :mode ("\\.py\\'" . python-mode))

(use-package rust-mode
  :ensure t
  :mode ("\\.rs\\'" . rust-mode))

(use-package scheme
  :ensure nil
  :mode ("\\.ss\\'" . scheme-mode))

(use-package sql-mode
  :ensure nil
  :mode ("\\.sql\\'" . sql-mode)
  :init
  (setq sql-input-ring-file-name (concat no-littering-var-directory "sql_history")))

(use-package toml-mode
  :ensure t
  :mode ("\\.toml\\'" . toml-mode))

(use-package typescript-mode
  :ensure t
  :mode ("\\.ts\\'" . typescript-mode))

(define-derived-mode vue-mode web-mode "Vue")
(add-to-list 'auto-mode-alist '("\\.vue\\'" . vue-mode))

(use-package web-mode
  :ensure t
  :mode (("\\.phtml\\'"     . web-mode)
         ("\\.php\\'"       . web-mode)
         ("\\.tpl\\'"       . web-mode)
         ("\\.[agj]sp\\'"   . web-mode)
         ("\\.as[cp]x\\'"   . web-mode)
         ("\\.erb\\'"       . web-mode)
         ("\\.mustache\\'"  . web-mode)
         ("\\.djhtml\\'"    . web-mode)
         ("\\.[t]?html?\\'" . web-mode))
  :init
  (setq org-src-lang-modes
        (append org-src-lang-modes
                '(("html" . web)))))

(use-package yaml-mode
  :ensure t
  :mode ("\\.ya?ml\\'" . yaml-mode))

(provide 'init-lang)
;;; init-lang.el ends here
